#ifndef IO_HPP
#define IO_HPP

#include "typedef.hpp"
#include <iostream>
#include <fstream>

class ProgramIO
{
private:
	PIOSTATUS stat = DONE;
	PIOTYPE type = NONE;

public:
	ProgramIO() = default;
	ProgramIO(PIOTYPE _type) : type(_type) {}
	~ProgramIO() = default;
	virtual void read() = 0;
	virtual void write() = 0;
	virtual void set(uint64_t) = 0;
	virtual uint64_t get() = 0;
	inline void setStat(PIOSTATUS _status) { stat = _status; }
	inline PIOSTATUS getStat() const { return stat; }
	inline PIOTYPE getType() const { return type; }
	virtual void run(Semaphore<std::pair<uint16_t, uint16_t>> &) = 0;
};
class SerialIn : public ProgramIO
{
private:
	uint8_t value;

public:
	SerialIn() = default;
	~SerialIn() = default;
	void read();
	void write();
	void set(uint64_t);
	uint64_t get();
	void run(Semaphore<std::pair<uint16_t, uint16_t>> &);
};
class SerialOut : public ProgramIO
{
private:
	uint8_t value;

public:
	SerialOut() = default;
	~SerialOut() = default;
	void read();
	void write();
	void set(uint64_t);
	uint64_t get();
	void run(Semaphore<std::pair<uint16_t, uint16_t>> &);
};
class MemoryIn : public ProgramIO
{
private:
	uint64_t maddr;
	uint16_t count;

public:
	MemoryIn() = default;
	~MemoryIn() = default;
	void read();
	void write();
	void set(uint64_t);
	uint64_t get();
	void run(Semaphore<std::pair<uint16_t, uint16_t>> &);
};
class MemoryOut : public ProgramIO
{
private:
	uint64_t maddr;
	uint16_t count;

public:
	MemoryOut() = default;
	~MemoryOut() = default;
	void read();
	void write();
	void set(uint64_t);
	uint64_t get();
	void run(Semaphore<std::pair<uint16_t, uint16_t>> &);
};
class FileIO : public ProgramIO
{
private:
	FILE *file;
	uint64_t maddr, faddr;

public:
	FileIO() = default;
	FileIO(const char *, PIOTYPE);
	~FileIO() = default;
	void read();
	void write();
	void set(uint64_t);
	uint64_t get();
	void run(Semaphore<std::pair<uint16_t, uint16_t>> &);
};
class Timer : public ProgramIO
{
private:
	uint64_t value;

public:
	Timer() = default;
	~Timer() = default;
	void read();
	void write();
	void set(uint64_t);
	uint64_t get();
	void run(Semaphore<std::pair<uint16_t, uint16_t>> &);
};

class IOChannel
{
private:
	std::map<uint16_t, ProgramIO *> mIO;
	IOMessage *msg;

public:
	IOChannel() = default;
	IOChannel(IOMessage *msg) : msg(msg) {}
	~IOChannel() = default;
	void init();
	void mount(std::string fname, PIOTYPE);
	void umount(uint16_t);
	void run();
};

#endif